Rewrote the EXAMINE command.
[pspp-builds.git] / src / math / percentiles.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2008 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 #include "percentiles.h"
19 #include <math/order-stats.h>
20
21
22 #include "gettext.h"
23 #define _(msgid) gettext (msgid)
24 #define N_(msgid) msgid
25
26 #include <libpspp/assertion.h>
27 #include <data/val-type.h>
28 #include <gl/xalloc.h>
29 #include <data/variable.h>
30 #include <data/casereader.h>
31
32
33 const char *const ptile_alg_desc[] = {
34   "",
35   N_("HAverage"),
36   N_("Weighted Average"),
37   N_("Rounded"),
38   N_("Empirical"),
39   N_("Empirical with averaging")
40 };
41
42
43
44 double
45 percentile_calculate (const struct percentile *ptl, enum pc_alg alg)
46 {
47   struct percentile *mutable = (struct percentile *) ptl;
48   const struct order_stats *os = &ptl->parent;
49
50   if ( ptl->g1 == SYSMIS)
51     mutable->g1 = (os->k[0].tc - os->k[0].cc) / os->k[0].c_p1;
52
53   if ( ptl->g1_star == SYSMIS)
54     mutable->g1_star = os->k[0].tc - os->k[0].cc;
55
56   if ( ptl->g2 == SYSMIS)
57     {
58       if ( os->k[1].c == 0 )
59         mutable->g2 = os->k[1].tc / os->k[1].c_p1;
60       else if ( os->k[1].c_p1 == 0 )
61         mutable->g2 = 0;
62       else
63         mutable->g2 = (os->k[1].tc - os->k[1].cc) / os->k[1].c_p1;
64     }
65
66   if ( ptl->g2_star == SYSMIS)
67     {
68       if ( os->k[1].c == 0 )
69         mutable->g2_star = os->k[1].tc;
70       else if ( os->k[1].c_p1 == 0 )
71         mutable->g2_star = 0;
72       else
73         mutable->g2_star = os->k[1].tc - os->k[1].cc;
74     }
75
76   switch (alg)
77     {
78     case PC_WAVERAGE:
79       if ( ptl->g1_star >= 1.0)
80         return os->k[0].y_p1;
81       else
82         {
83           double a = ( os->k[0].y == SYSMIS ) ? 0 : os->k[0].y;
84
85           if (os->k[0].c_p1 >= 1.0)
86             return (1 - ptl->g1_star) * a + ptl->g1_star * os->k[0].y_p1;
87           else
88             return (1 - ptl->g1) * a + ptl->g1 * os->k[0].y_p1;
89         }
90       break;
91
92     case PC_ROUND:
93       {
94         double a = ( os->k[0].y == SYSMIS ) ? 0 : os->k[0].y;
95
96         if (os->k[0].c_p1 >= 1.0)
97           return (ptl->g1_star < 0.5) ? a : os->k[0].y_p1;
98         else
99           return (ptl->g1 < 0.5) ? a : os->k[0].y_p1;
100       }
101       break;
102
103     case PC_EMPIRICAL:
104       if ( ptl->g1_star == 0 )
105         return os->k[0].y;
106       else
107         return os->k[0].y_p1;
108       break;
109
110     case PC_HAVERAGE:
111       if ( ptl->g2_star >= 1.0)
112         {
113           return os->k[1].y_p1;
114         }
115       else
116         {
117           double a = ( os->k[1].y == SYSMIS ) ? 0 : os->k[1].y;
118
119           if ( os->k[1].c_p1 >= 1.0)
120             {
121               if ( ptl->g2_star == 0)
122                 return os->k[1].y;
123
124               return (1 - ptl->g2_star) * a + ptl->g2_star * os->k[1].y_p1;
125             }
126           else
127             {
128               return (1 - ptl->g2) * a + ptl->g2 * os->k[1].y_p1;
129             }
130         }
131
132       break;
133
134     case PC_AEMPIRICAL:
135       if ( ptl->g1_star == 0 )
136         return (os->k[0].y + os->k[0].y_p1)/ 2.0;
137       else
138         return os->k[0].y_p1;
139       break;
140
141     default:
142       NOT_REACHED ();
143       break;
144     }
145
146   NOT_REACHED ();
147
148   return SYSMIS;
149 }
150
151
152 static void
153 destroy (struct statistic *stat)
154 {
155   struct order_stats *os = (struct order_stats *) stat;
156   free (os->k);
157   free (os);
158 }
159
160
161 struct order_stats *
162 percentile_create (double p, double W)
163 {
164   struct percentile *ptl = xzalloc (sizeof (*ptl));
165   struct order_stats *os = (struct order_stats *) ptl;
166   struct statistic *stat = (struct statistic *) ptl;
167
168   assert (p >= 0);
169   assert (p <= 1.0);
170
171   ptl->ptile = p;
172
173   os->n_k = 2;
174   os->k = xcalloc (sizeof (*os->k), 2);
175   os->k[0].tc = W * p;
176   os->k[1].tc = (W + 1.0) * p;
177
178   ptl->g1 = ptl->g1_star = SYSMIS;
179   ptl->g2 = ptl->g2_star = SYSMIS;
180
181   os->k[1].y_p1 = os->k[1].y = SYSMIS;
182   os->k[0].y_p1 = os->k[0].y = SYSMIS;
183
184   stat->destroy = destroy;
185
186   return os;
187 }
188
189 #if 0
190 void
191 percentile_dump (const struct percentile *ptl)
192 {
193   printf ("Percentile %g:\n\tk1: ", ptl->ptile);
194
195   dump_os_k1 ((const struct os *)ptl);
196   printf ("\tk2: ");
197   dump_os_k2 ((const struct os *)ptl);
198   printf ("\n");
199 }
200 #endif