Added files in src/output
[pspp-builds.git] / src / output / charts / box-whisker.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 2004 Free Software Foundation, Inc.
3    Written by John Darrington <john@darrington.wattle.id.au>
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., 51 Franklin Street, Fifth Floor, Boston, MA
18    02110-1301, USA. */
19
20
21 #include "chart.h"
22 #include "chart-geometry.h"
23 #include <math.h>
24 #include <assert.h>
25 #include "misc.h"
26
27 #include "factor-stats.h"
28 #include "box-whisker.h"
29 #include "plot-chart.h"
30
31
32 /* Draw a box-and-whiskers plot
33 */
34
35 /* Draw an outlier on the plot CH
36  * at CENTRELINE
37  * The outlier is in (*wvp)[idx]
38  * If EXTREME is non zero, then consider it to be an extreme
39  * value
40  */
41 void 
42 draw_outlier(struct chart *ch, double centreline, 
43              struct weighted_value **wvp, 
44              int idx,
45              short extreme);
46
47
48 void 
49 draw_outlier(struct chart *ch, double centreline, 
50              struct weighted_value **wvp, 
51              int idx,
52              short extreme
53              )
54 {
55   char label[10];
56
57 #define MARKER_CIRCLE 4
58 #define MARKER_STAR 3
59
60   pl_fmarker_r(ch->lp,
61                centreline,
62                ch->data_bottom + 
63                (wvp[idx]->v.f - ch->y_min ) * ch->ordinate_scale,
64                extreme?MARKER_STAR:MARKER_CIRCLE,
65                20);
66
67   pl_moverel_r(ch->lp, 10,0);
68
69   snprintf(label, 10, "%d", wvp[idx]->case_nos->num);
70   
71   pl_alabel_r(ch->lp, 'l', 'c', label);
72
73 }
74
75
76 void 
77 boxplot_draw_boxplot(struct chart *ch,
78                      double box_centre, 
79                      double box_width,
80                      struct metrics *m,
81                      const char *name)
82 {
83   double whisker[2];
84   int i;
85
86   const double *hinge = m->hinge;
87   struct weighted_value **wvp = m->wvp;
88   const int n_data = m->n_data;
89
90   const double step = (hinge[2] - hinge[0]) * 1.5;
91
92
93   const double box_left = box_centre - box_width / 2.0;
94
95   const double box_right = box_centre + box_width / 2.0;
96
97
98   const double box_bottom = 
99     ch->data_bottom + ( hinge[0] - ch->y_min ) * ch->ordinate_scale;
100
101
102   const double box_top = 
103     ch->data_bottom + ( hinge[2] - ch->y_min ) * ch->ordinate_scale;
104
105   assert(m);
106
107   /* Can't really draw a boxplot if there's no data */
108   if ( n_data == 0 ) 
109           return ;
110
111   whisker[1] = hinge[2];
112   whisker[0] = wvp[0]->v.f;
113
114   for ( i = 0 ; i < n_data ; ++i ) 
115     {
116       if ( hinge[2] + step >  wvp[i]->v.f) 
117         whisker[1] = wvp[i]->v.f;
118
119       if ( hinge[0] - step >  wvp[i]->v.f) 
120         whisker[0] = wvp[i]->v.f;
121     
122     }
123     
124   {
125   const double bottom_whisker = 
126     ch->data_bottom + ( whisker[0] - ch->y_min ) * ch->ordinate_scale;
127
128   const double top_whisker = 
129     ch->data_bottom + ( whisker[1] - ch->y_min ) * ch->ordinate_scale;
130
131         
132   pl_savestate_r(ch->lp);
133
134
135   /* Draw the box */
136   pl_savestate_r(ch->lp);
137   pl_fillcolorname_r(ch->lp,ch->fill_colour);
138   pl_filltype_r(ch->lp,1);
139   pl_fbox_r(ch->lp, 
140             box_left,
141             box_bottom,
142             box_right,
143             box_top);
144
145   pl_restorestate_r(ch->lp);
146
147
148   
149   /* Draw the median */
150   pl_savestate_r(ch->lp);
151   pl_linewidth_r(ch->lp,5);
152   pl_fline_r(ch->lp, 
153              box_left, 
154              ch->data_bottom + ( hinge[1] - ch->y_min ) * ch->ordinate_scale,
155              box_right,   
156              ch->data_bottom + ( hinge[1] - ch->y_min ) * ch->ordinate_scale);
157   pl_restorestate_r(ch->lp);
158
159
160   /* Draw the bottom whisker */
161   pl_fline_r(ch->lp, 
162              box_left, 
163              bottom_whisker,
164              box_right,   
165              bottom_whisker);
166
167   /* Draw top whisker */
168   pl_fline_r(ch->lp, 
169              box_left, 
170              top_whisker,
171              box_right,   
172              top_whisker);
173
174
175
176   /* Draw centre line.
177      (bottom half) */
178   pl_fline_r(ch->lp, 
179              box_centre, bottom_whisker,
180              box_centre, box_bottom);
181
182   /* (top half) */
183   pl_fline_r(ch->lp, 
184              box_centre, top_whisker,
185              box_centre, box_top);
186   }
187
188   /* Draw outliers */
189   for ( i = 0 ; i < n_data ; ++i ) 
190     {
191       if ( wvp[i]->v.f >= hinge[2] + step ) 
192         draw_outlier(ch, box_centre, wvp, i, 
193                      ( wvp[i]->v.f > hinge[2] + 2 * step ) 
194                      );
195
196       if ( wvp[i]->v.f <= hinge[0] - step ) 
197         draw_outlier(ch, box_centre, wvp, i, 
198                      ( wvp[i]->v.f < hinge[0] - 2 * step )
199                      );
200     }
201
202
203   /* Draw  tick  mark on x axis */
204   draw_tick(ch, TICK_ABSCISSA, box_centre - ch->data_left, name);
205
206   pl_restorestate_r(ch->lp);
207
208 }
209
210
211
212 void
213 boxplot_draw_yscale(struct chart *ch , double y_max, double y_min)
214 {
215   double y_tick;
216   double d;
217
218   if ( !ch ) 
219      return ;
220
221   ch->y_max  = y_max;
222   ch->y_min  = y_min;
223
224   y_tick = chart_rounded_tick(fabs(ch->y_max - ch->y_min) / 5.0);
225
226   ch->y_min = (ceil( ch->y_min  / y_tick ) - 1.0  ) * y_tick;
227       
228   ch->y_max = ( floor( ch->y_max  / y_tick ) + 1.0  ) * y_tick;
229
230   ch->ordinate_scale = fabs(ch->data_top - ch->data_bottom) 
231     / fabs(ch->y_max - ch->y_min) ;
232
233
234   /* Move to data bottom-left */
235   pl_move_r(ch->lp, 
236             ch->data_left, ch->data_bottom);
237
238   for ( d = ch->y_min; d <= ch->y_max ; d += y_tick )
239     {
240       draw_tick (ch, TICK_ORDINATE, (d - ch->y_min ) * ch->ordinate_scale, "%g", d);
241     }
242
243 }