fa52476e8ac7775e1606f431acfb529a2951d613
[pspp-builds.git] / src / 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., 59 Temple Place - Suite 330, Boston, MA
18    02111-1307, USA. */
19
20
21 #include "chart.h"
22 #include <math.h>
23
24 /* Draw a box-and-whiskers plot
25 */
26
27 struct data_stats 
28 {
29   double ptile0  ;
30   double ptile25 ;
31   double median  ;
32   double ptile75 ;
33
34   double ptile100;
35
36   double outlier ;
37 };
38
39
40 const struct data_stats stats1 = {
41   40,
42   45,
43   54,
44   60,
45   70,
46
47   33
48 };
49
50 const struct data_stats stats2 = {
51   30,
52   40,
53   45,
54   54,
55   60,
56
57
58   72
59 };
60
61
62
63
64
65 static const double y_min = 25;
66 static const double y_max = 75;
67 static const double y_tick = 10;
68
69
70
71 #define min(A,B) ((A>B)?B:A)
72
73
74 void draw_box_and_whiskers(struct chart *ch,
75                            double box_centre, const struct data_stats *s, 
76                            const char *name);
77
78
79 static double ordinate_scale;
80
81 void
82 draw_box_whisker_chart(struct chart *ch, const char *title)
83 {
84   double d;
85
86   ordinate_scale = fabs(ch->data_top -  ch->data_bottom) / fabs(y_max - y_min) ;
87
88
89   chart_write_title(ch, title);
90
91   
92
93   /* Move to data bottom-left */
94   pl_move_r(ch->lp, 
95                   ch->data_left, ch->data_bottom);
96
97   for ( d = y_min; d <= y_max ; d += y_tick )
98     {
99       draw_tick (ch, TICK_ORDINATE, (d - y_min ) * ordinate_scale, "%g", d);
100     }
101
102   draw_box_and_whiskers(ch,
103                         ch->data_left  + 1.0/4.0 * (ch->data_right - ch->data_left) ,
104                         &stats1,"Stats1"
105                         );
106
107   draw_box_and_whiskers(ch,
108                         ch->data_left + 3.0/4.0 * (ch->data_right - ch->data_left),
109                         &stats2,"Stats2"
110                         );
111
112
113 }
114
115
116 void 
117 draw_box_and_whiskers(struct chart *ch,
118                       double box_centre, const struct data_stats *s,
119                       const char *name)
120 {
121
122   const double box_width = (ch->data_right - ch->data_left) / 4.0;
123
124   const double box_left = box_centre - box_width / 2.0;
125
126   const double box_right = box_centre + box_width / 2.0;
127
128
129   const double box_bottom = 
130     ch->data_bottom + ( s->ptile25 - y_min ) * ordinate_scale;
131
132
133   const double box_top = 
134     ch->data_bottom + ( s->ptile75 - y_min ) * ordinate_scale;
135
136
137   const double iq_range = s->ptile75 - s->ptile25;
138
139   const double bottom_whisker = 
140     ch->data_bottom + (min(s->ptile0,s->ptile25 + iq_range*1.5)  - y_min ) * 
141     ordinate_scale;
142
143   const double top_whisker =
144     ch->data_bottom + (min(s->ptile100,s->ptile75 + iq_range*1.5)  - y_min ) * 
145     ordinate_scale;
146         
147   pl_savestate_r(ch->lp);
148
149
150   /* Draw the box */
151   pl_savestate_r(ch->lp);
152   pl_fillcolorname_r(ch->lp,ch->fill_colour);
153   pl_filltype_r(ch->lp,1);
154   pl_fbox_r(ch->lp, 
155             box_left,
156             box_bottom,
157             box_right,
158             box_top);
159
160   pl_restorestate_r(ch->lp);
161
162
163   
164   /* Draw the median */
165   pl_savestate_r(ch->lp);
166   pl_linewidth_r(ch->lp,5);
167   pl_fline_r(ch->lp, 
168              box_left, 
169              ch->data_bottom + ( s->median - y_min ) * ordinate_scale,
170              box_right,   
171              ch->data_bottom + ( s->median - y_min ) * ordinate_scale);
172   pl_restorestate_r(ch->lp);
173
174
175   /* Draw the bottom whisker */
176   pl_fline_r(ch->lp, 
177              box_left, 
178              bottom_whisker,
179              box_right,   
180              bottom_whisker);
181
182   /* Draw top whisker */
183   pl_fline_r(ch->lp, 
184              box_left, 
185              top_whisker,
186              box_right,   
187              top_whisker);
188
189
190   /* Draw centre line.
191      (bottom half) */
192   pl_fline_r(ch->lp, 
193              box_centre, bottom_whisker,
194              box_centre, box_bottom);
195
196   /* (top half) */
197   pl_fline_r(ch->lp, 
198              box_centre, top_whisker,
199              box_centre, box_top);
200
201
202   /* Draw an outlier */
203   pl_fcircle_r(ch->lp,
204                box_centre, 
205                ch->data_bottom + (s->outlier - y_min ) * ordinate_scale,
206                5);
207
208   pl_moverel_r(ch->lp, 10,0);
209   pl_alabel_r(ch->lp,'l','c',"123");
210
211
212   /* Draw  tick  mark on x axis */
213   draw_tick(ch, TICK_ABSCISSA, box_centre - ch->data_left, name);
214
215   pl_restorestate_r(ch->lp);
216
217 }
218