e117cc366cbfd24aefacd5631eca45a1a81c2e0c
[pspp] / src / language / stats / mann-whitney.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2010, 2011 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 "language/stats/mann-whitney.h"
20
21 #include <gsl/gsl_cdf.h>
22
23 #include "data/case.h"
24 #include "data/casereader.h"
25 #include "data/dataset.h"
26 #include "data/dictionary.h"
27 #include "data/format.h"
28 #include "data/variable.h"
29 #include "libpspp/cast.h"
30 #include "libpspp/misc.h"
31 #include "math/sort.h"
32 #include "output/tab.h"
33
34 /* Calculates the adjustment necessary for tie compensation */
35 static void
36 distinct_callback (double v UNUSED, casenumber t, double w UNUSED, void *aux)
37 {
38   double *tiebreaker = aux;
39
40   *tiebreaker += (pow3 (t) - t) / 12.0;
41 }
42
43 struct mw
44 {
45   double rank_sum[2];
46   double n[2];
47
48   double u;  /* The Mann-Whitney U statistic */
49   double w;  /* The Wilcoxon Rank Sum W statistic */
50   double z;
51 };
52
53 static void show_ranks_box (const struct n_sample_test *nst, const struct mw *mw);
54 static void show_statistics_box (const struct n_sample_test *nst, const struct mw *mw, bool exact);
55
56
57
58 static bool
59 belongs_to_test (const struct ccase *c, void *aux)
60 {
61   const struct n_sample_test *nst = aux;
62
63   const union value *group = case_data (c, nst->indep_var);
64   const size_t group_var_width = var_get_width (nst->indep_var);
65
66   if ( value_equal (group, &nst->val1, group_var_width))
67     return true;
68
69   if ( value_equal (group, &nst->val2, group_var_width))
70     return true;
71
72   return false;
73 }
74
75
76
77 void
78 mann_whitney_execute (const struct dataset *ds,
79                       struct casereader *input,
80                       enum mv_class exclude,
81                       const struct npar_test *test,
82                       bool exact,
83                       double timer UNUSED)
84 {
85   int i;
86   const struct dictionary *dict = dataset_dict (ds);
87   const struct n_sample_test *nst = UP_CAST (test, const struct n_sample_test, parent);
88
89   const struct caseproto *proto = casereader_get_proto (input);
90   size_t rank_idx = caseproto_get_n_widths (proto);
91
92   struct mw *mw = xcalloc (nst->n_vars, sizeof *mw);
93
94   for (i = 0; i < nst->n_vars; ++i)
95     {
96       double tiebreaker = 0.0;
97       bool warn = true;
98       enum rank_error rerr = 0;
99       struct casereader *rr;
100       struct ccase *c;
101       const struct variable *var = nst->vars[i];
102
103       struct casereader *reader =
104         casereader_create_filter_func (casereader_clone (input),
105                                        belongs_to_test,
106                                        NULL,
107                                        CONST_CAST (struct n_sample_test *, nst),
108                                        NULL);
109
110       reader = casereader_create_filter_missing (reader, &var, 1,
111                                                  exclude,
112                                                  NULL, NULL);
113
114       reader = sort_execute_1var (reader, var);
115
116       rr = casereader_create_append_rank (reader, var,
117                                           dict_get_weight (dict),
118                                           &rerr,
119                                           distinct_callback, &tiebreaker);
120
121       for (; (c = casereader_read (rr)); case_unref (c))
122         {
123           const union value *group = case_data (c, nst->indep_var);
124           const size_t group_var_width = var_get_width (nst->indep_var);
125           const double rank = case_data_idx (c, rank_idx)->f;
126
127           if ( value_equal (group, &nst->val1, group_var_width))
128             {
129               mw[i].rank_sum[0] += rank;
130               mw[i].n[0] += dict_get_case_weight (dict, c, &warn);
131             }
132           else if ( value_equal (group, &nst->val2, group_var_width))
133             {
134               mw[i].rank_sum[1] += rank;
135               mw[i].n[1] += dict_get_case_weight (dict, c, &warn);
136             }
137         }
138       casereader_destroy (rr);
139
140       {
141         double n;
142         double denominator;
143         struct mw *mwv = &mw[i];
144
145         mwv->u = mwv->n[0] * mwv->n[1] ;
146         mwv->u += mwv->n[0] * (mwv->n[0] + 1) / 2.0;
147         mwv->u -= mwv->rank_sum[0];
148
149         mwv->w = mwv->rank_sum[1];
150         if ( mwv->u > mwv->n[0] * mwv->n[1] / 2.0)
151           {
152             mwv->u =  mwv->n[0] * mwv->n[1] - mwv->u;
153             mwv->w = mwv->rank_sum[0];
154           }
155         mwv->z = mwv->u - mwv->n[0] * mwv->n[1] / 2.0;
156         n = mwv->n[0] + mwv->n[1];
157         denominator = pow3(n) - n;
158         denominator /= 12;
159         denominator -= tiebreaker;
160         denominator *= mwv->n[0] * mwv->n[1];
161         denominator /= n * (n - 1);
162
163         mwv->z /= sqrt (denominator);
164       }
165     }
166   casereader_destroy (input);
167
168   show_ranks_box (nst, mw);
169   show_statistics_box (nst, mw, exact);
170
171   free (mw);
172 }
173
174 \f
175
176 #include "gettext.h"
177 #define _(msgid) gettext (msgid)
178
179 static void
180 show_ranks_box (const struct n_sample_test *nst, const struct mw *mwv)
181 {
182   int i;
183   const int row_headers = 1;
184   const int column_headers = 2;
185   struct tab_table *table =
186     tab_create (row_headers + 7, column_headers + nst->n_vars);
187
188   struct string g1str, g2str;;
189   ds_init_empty (&g1str);
190   var_append_value_name (nst->indep_var, &nst->val1, &g1str);
191
192   ds_init_empty (&g2str);
193   var_append_value_name (nst->indep_var, &nst->val2, &g2str);
194
195   tab_headers (table, row_headers, 0, column_headers, 0);
196
197   tab_title (table, _("Ranks"));
198
199   /* Vertical lines inside the box */
200   tab_box (table, 1, 0, -1, TAL_1,
201            row_headers, 0, tab_nc (table) - 1, tab_nr (table) - 1 );
202
203   /* Box around the table */
204   tab_box (table, TAL_2, TAL_2, -1, -1,
205            0,  0, tab_nc (table) - 1, tab_nr (table) - 1 );
206
207   tab_hline (table, TAL_2, 0, tab_nc (table) -1, column_headers);
208   tab_vline (table, TAL_2, row_headers, 0, tab_nr (table) - 1);
209
210   tab_hline (table, TAL_1, row_headers, tab_nc (table) -1, 1);
211
212   tab_text (table, 1, 1, TAT_TITLE | TAB_CENTER, ds_cstr (&g1str));
213   tab_text (table, 2, 1, TAT_TITLE | TAB_CENTER, ds_cstr (&g2str));
214   tab_text (table, 3, 1, TAT_TITLE | TAB_CENTER, _("Total"));
215   tab_joint_text (table, 1, 0, 3, 0,
216                   TAT_TITLE | TAB_CENTER, _("N"));
217   tab_vline (table, TAL_2, 4, 0, tab_nr (table) - 1);
218
219   tab_text (table, 4, 1, TAT_TITLE | TAB_CENTER, ds_cstr (&g1str));
220   tab_text (table, 5, 1, TAT_TITLE | TAB_CENTER, ds_cstr (&g2str));
221   tab_joint_text (table, 4, 0, 5, 0,
222                   TAT_TITLE | TAB_CENTER, _("Mean Rank"));
223   tab_vline (table, TAL_2, 6, 0, tab_nr (table) - 1);
224
225   tab_text (table, 6, 1, TAT_TITLE | TAB_CENTER, ds_cstr (&g1str));
226   tab_text (table, 7, 1, TAT_TITLE | TAB_CENTER, ds_cstr (&g2str));
227   tab_joint_text (table, 6, 0, 7, 0,
228                   TAT_TITLE | TAB_CENTER, _("Sum of Ranks"));
229
230   ds_destroy (&g1str);
231   ds_destroy (&g2str);
232
233   for (i = 0 ; i < nst->n_vars ; ++i)
234     {
235       const struct mw *mw = &mwv[i];
236       tab_text (table, 0, column_headers + i, TAT_TITLE,
237                 var_to_string (nst->vars[i]));
238
239       tab_double (table, 1, column_headers + i, 0,
240                   mw->n[0], NULL, RC_OTHER);
241
242       tab_double (table, 2, column_headers + i, 0,
243                   mw->n[1], NULL, RC_OTHER);
244
245       tab_double (table, 3, column_headers + i, 0,
246                   mw->n[1] + mw->n[0], NULL, RC_OTHER);
247
248       /* Mean Ranks */
249       tab_double (table, 4, column_headers + i, 0,
250                   mw->rank_sum[0] / mw->n[0], NULL, RC_OTHER);
251
252       tab_double (table, 5, column_headers + i, 0,
253                   mw->rank_sum[1] / mw->n[1], NULL, RC_OTHER);
254
255       /* Sum of Ranks */
256       tab_double (table, 6, column_headers + i, 0,
257                   mw->rank_sum[0], NULL, RC_OTHER);
258
259       tab_double (table, 7, column_headers + i, 0,
260                   mw->rank_sum[1], NULL, RC_OTHER);
261     }
262
263   tab_submit (table);
264 }
265
266 static void
267 show_statistics_box (const struct n_sample_test *nst, const struct mw *mwv, bool exact)
268 {
269   int i;
270   const int row_headers = 1;
271   const int column_headers = 1;
272   struct tab_table *table =
273     tab_create (row_headers + (exact ? 6 : 4), column_headers + nst->n_vars);
274
275   tab_headers (table, row_headers, 0, column_headers, 0);
276
277   tab_title (table, _("Test Statistics"));
278
279   /* Vertical lines inside the box */
280   tab_box (table, 1, 0, -1, TAL_1,
281            row_headers, 0, tab_nc (table) - 1, tab_nr (table) - 1 );
282
283   /* Box around the table */
284   tab_box (table, TAL_2, TAL_2, -1, -1,
285            0,  0, tab_nc (table) - 1, tab_nr (table) - 1 );
286
287   tab_hline (table, TAL_2, 0, tab_nc (table) -1, column_headers);
288   tab_vline (table, TAL_2, row_headers, 0, tab_nr (table) - 1);
289
290   tab_text (table, 1, 0, TAT_TITLE | TAB_CENTER, _("Mann-Whitney U"));
291   tab_text (table, 2, 0, TAT_TITLE | TAB_CENTER, _("Wilcoxon W"));
292   tab_text (table, 3, 0, TAT_TITLE | TAB_CENTER, _("Z"));
293   tab_text (table, 4, 0, TAT_TITLE | TAB_CENTER, _("Asymp. Sig. (2-tailed)"));
294
295   if (exact)
296     {
297       tab_text (table, 5, 0, TAT_TITLE | TAB_CENTER, _("Exact Sig. (2-tailed)"));
298       tab_text (table, 6, 0, TAT_TITLE | TAB_CENTER, _("Point Probability"));
299     }
300
301   for (i = 0 ; i < nst->n_vars ; ++i)
302     {
303       const struct mw *mw = &mwv[i];
304
305       tab_text (table, 0, column_headers + i, TAT_TITLE,
306                 var_to_string (nst->vars[i]));
307
308       tab_double (table, 1, column_headers + i, 0,
309                   mw->u, NULL, RC_OTHER);
310
311       tab_double (table, 2, column_headers + i, 0,
312                   mw->w, NULL, RC_OTHER);
313
314       tab_double (table, 3, column_headers + i, 0,
315                   mw->z, NULL, RC_OTHER);
316
317       tab_double (table, 4, column_headers + i, 0,
318                   2.0 * gsl_cdf_ugaussian_P (mw->z), NULL, RC_PVALUE);
319     }
320
321   tab_submit (table);
322 }