1 /* Pspp - a program for statistical analysis.
2 Copyright (C) 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
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.
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.
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/>. */
21 #include "language/stats/wilcoxon.h"
23 #include <gsl/gsl_cdf.h>
26 #include "data/casereader.h"
27 #include "data/casewriter.h"
28 #include "data/dataset.h"
29 #include "data/dictionary.h"
30 #include "data/format.h"
31 #include "data/subcase.h"
32 #include "data/variable.h"
33 #include "libpspp/assertion.h"
34 #include "libpspp/message.h"
35 #include "libpspp/misc.h"
36 #include "math/sort.h"
37 #include "math/wilcoxon-sig.h"
38 #include "output/tab.h"
40 #include "gl/minmax.h"
41 #include "gl/xalloc.h"
44 append_difference (const struct ccase *c, casenumber n UNUSED, void *aux)
46 const variable_pair *vp = aux;
48 return case_data (c, (*vp)[0])->f - case_data (c, (*vp)[1])->f;
51 static void show_ranks_box (const struct wilcoxon_state *,
52 const struct two_sample_test *,
53 const struct dictionary *);
55 static void show_tests_box (const struct wilcoxon_state *,
56 const struct two_sample_test *,
57 bool exact, double timer);
62 distinct_callback (double v UNUSED, casenumber n, double w UNUSED, void *aux)
64 struct wilcoxon_state *ws = aux;
66 ws->tiebreaker += pow3 (n) - n;
72 wilcoxon_execute (const struct dataset *ds,
73 struct casereader *input,
74 enum mv_class exclude,
75 const struct npar_test *test,
81 const struct dictionary *dict = dataset_dict (ds);
82 const struct two_sample_test *t2s = UP_CAST (test, const struct two_sample_test, parent);
84 struct wilcoxon_state *ws = xcalloc (t2s->n_pairs, sizeof *ws);
85 const struct variable *weight = dict_get_weight (dict);
86 struct variable *weightx = dict_create_internal_var (WEIGHT_IDX, 0);
87 struct caseproto *proto;
90 casereader_create_filter_weight (input, dict, &warn, NULL);
92 proto = caseproto_create ();
93 proto = caseproto_add_width (proto, 0);
94 proto = caseproto_add_width (proto, 0);
96 proto = caseproto_add_width (proto, 0);
98 for (i = 0 ; i < t2s->n_pairs; ++i )
100 struct casereader *r = casereader_clone (input);
101 struct casewriter *writer;
103 struct subcase ordering;
104 variable_pair *vp = &t2s->pairs[i];
106 ws[i].sign = dict_create_internal_var (0, 0);
107 ws[i].absdiff = dict_create_internal_var (1, 0);
109 r = casereader_create_filter_missing (r, *vp, 2,
113 subcase_init_var (&ordering, ws[i].absdiff, SC_ASCEND);
114 writer = sort_create_writer (&ordering, proto);
115 subcase_destroy (&ordering);
117 for (; (c = casereader_read (r)) != NULL; case_unref (c))
119 struct ccase *output = case_create (proto);
120 double d = append_difference (c, 0, vp);
124 case_data_rw (output, ws[i].sign)->f = 1.0;
129 case_data_rw (output, ws[i].sign)->f = -1.0;
135 w = case_data (c, weight)->f;
137 /* Central point values should be dropped */
143 case_data_rw (output, ws[i].absdiff)->f = fabs (d);
146 case_data_rw (output, weightx)->f = case_data (c, weight)->f;
148 casewriter_write (writer, output);
150 casereader_destroy (r);
151 ws[i].reader = casewriter_make_reader (writer);
153 caseproto_unref (proto);
155 for (i = 0 ; i < t2s->n_pairs; ++i )
157 struct casereader *rr ;
159 enum rank_error err = 0;
161 rr = casereader_create_append_rank (ws[i].reader, ws[i].absdiff,
162 weight ? weightx : NULL, &err,
163 distinct_callback, &ws[i]
166 for (; (c = casereader_read (rr)) != NULL; case_unref (c))
168 double sign = case_data (c, ws[i].sign)->f;
169 double rank = case_data_idx (c, weight ? 3 : 2)->f;
172 w = case_data (c, weightx)->f;
176 ws[i].positives.sum += rank * w;
177 ws[i].positives.n += w;
181 ws[i].negatives.sum += rank * w;
182 ws[i].negatives.n += w;
188 casereader_destroy (rr);
191 casereader_destroy (input);
193 dict_destroy_internal_var (weightx);
195 show_ranks_box (ws, t2s, dict);
196 show_tests_box (ws, t2s, exact, timer);
198 for (i = 0 ; i < t2s->n_pairs; ++i )
200 dict_destroy_internal_var (ws[i].sign);
201 dict_destroy_internal_var (ws[i].absdiff);
211 #define _(msgid) gettext (msgid)
214 show_ranks_box (const struct wilcoxon_state *ws,
215 const struct two_sample_test *t2s,
216 const struct dictionary *dict)
220 const struct variable *wv = dict_get_weight (dict);
221 const struct fmt_spec *wfmt = wv ? var_get_print_format (wv) : & F_8_0;
223 struct tab_table *table = tab_create (5, 1 + 4 * t2s->n_pairs);
225 tab_title (table, _("Ranks"));
227 tab_headers (table, 2, 0, 1, 0);
229 /* Vertical lines inside the box */
230 tab_box (table, 0, 0, -1, TAL_1,
231 1, 0, tab_nc (table) - 1, tab_nr (table) - 1 );
233 /* Box around entire table */
234 tab_box (table, TAL_2, TAL_2, -1, -1,
235 0, 0, tab_nc (table) - 1, tab_nr (table) - 1 );
238 tab_text (table, 2, 0, TAB_CENTER, _("N"));
239 tab_text (table, 3, 0, TAB_CENTER, _("Mean Rank"));
240 tab_text (table, 4, 0, TAB_CENTER, _("Sum of Ranks"));
243 for (i = 0 ; i < t2s->n_pairs; ++i)
245 variable_pair *vp = &t2s->pairs[i];
247 struct string pair_name;
248 ds_init_cstr (&pair_name, var_to_string ((*vp)[0]));
249 ds_put_cstr (&pair_name, " - ");
250 ds_put_cstr (&pair_name, var_to_string ((*vp)[1]));
252 tab_text (table, 1, 1 + i * 4, TAB_LEFT, _("Negative Ranks"));
253 tab_text (table, 1, 2 + i * 4, TAB_LEFT, _("Positive Ranks"));
254 tab_text (table, 1, 3 + i * 4, TAB_LEFT, _("Ties"));
255 tab_text (table, 1, 4 + i * 4, TAB_LEFT, _("Total"));
257 tab_hline (table, TAL_1, 0, tab_nc (table) - 1, 1 + i * 4);
260 tab_text (table, 0, 1 + i * 4, TAB_LEFT, ds_cstr (&pair_name));
261 ds_destroy (&pair_name);
265 tab_double (table, 2, 1 + i * 4, TAB_RIGHT, ws[i].negatives.n, wfmt);
266 tab_double (table, 2, 2 + i * 4, TAB_RIGHT, ws[i].positives.n, wfmt);
267 tab_double (table, 2, 3 + i * 4, TAB_RIGHT, ws[i].n_zeros, wfmt);
269 tab_double (table, 2, 4 + i * 4, TAB_RIGHT,
270 ws[i].n_zeros + ws[i].positives.n + ws[i].negatives.n, wfmt);
273 tab_double (table, 4, 1 + i * 4, TAB_RIGHT, ws[i].negatives.sum, NULL);
274 tab_double (table, 4, 2 + i * 4, TAB_RIGHT, ws[i].positives.sum, NULL);
278 tab_double (table, 3, 1 + i * 4, TAB_RIGHT,
279 ws[i].negatives.sum / (double) ws[i].negatives.n, NULL);
281 tab_double (table, 3, 2 + i * 4, TAB_RIGHT,
282 ws[i].positives.sum / (double) ws[i].positives.n, NULL);
286 tab_hline (table, TAL_2, 0, tab_nc (table) - 1, 1);
287 tab_vline (table, TAL_2, 2, 0, tab_nr (table) - 1);
295 show_tests_box (const struct wilcoxon_state *ws,
296 const struct two_sample_test *t2s,
302 struct tab_table *table = tab_create (1 + t2s->n_pairs, exact ? 5 : 3);
304 tab_title (table, _("Test Statistics"));
306 tab_headers (table, 1, 0, 1, 0);
308 /* Vertical lines inside the box */
309 tab_box (table, 0, 0, -1, TAL_1,
310 0, 0, tab_nc (table) - 1, tab_nr (table) - 1 );
312 /* Box around entire table */
313 tab_box (table, TAL_2, TAL_2, -1, -1,
314 0, 0, tab_nc (table) - 1, tab_nr (table) - 1 );
317 tab_text (table, 0, 1, TAB_LEFT, _("Z"));
318 tab_text (table, 0, 2, TAB_LEFT, _("Asymp. Sig. (2-tailed)"));
322 tab_text (table, 0, 3, TAB_LEFT, _("Exact Sig. (2-tailed)"));
323 tab_text (table, 0, 4, TAB_LEFT, _("Exact Sig. (1-tailed)"));
326 tab_text (table, 0, 5, TAB_LEFT, _("Point Probability"));
330 for (i = 0 ; i < t2s->n_pairs; ++i)
333 double n = ws[i].positives.n + ws[i].negatives.n;
334 variable_pair *vp = &t2s->pairs[i];
336 struct string pair_name;
337 ds_init_cstr (&pair_name, var_to_string ((*vp)[0]));
338 ds_put_cstr (&pair_name, " - ");
339 ds_put_cstr (&pair_name, var_to_string ((*vp)[1]));
342 tab_text (table, 1 + i, 0, TAB_CENTER, ds_cstr (&pair_name));
343 ds_destroy (&pair_name);
345 z = MIN (ws[i].positives.sum, ws[i].negatives.sum);
346 z -= n * (n + 1)/ 4.0;
348 z /= sqrt (n * (n + 1) * (2*n + 1)/24.0 - ws[i].tiebreaker / 48.0);
350 tab_double (table, 1 + i, 1, TAB_RIGHT, z, NULL);
352 tab_double (table, 1 + i, 2, TAB_RIGHT,
353 2.0 * gsl_cdf_ugaussian_P (z),
358 double p = LevelOfSignificanceWXMPSR (ws[i].positives.sum, n);
361 msg (MW, _("Too many pairs to calculate exact significance."));
365 tab_double (table, 1 + i, 3, TAB_RIGHT, p, NULL);
366 tab_double (table, 1 + i, 4, TAB_RIGHT, p / 2.0, NULL);
371 tab_hline (table, TAL_2, 0, tab_nc (table) - 1, 1);
372 tab_vline (table, TAL_2, 1, 0, tab_nr (table) - 1);